package com.lele.controller;

import com.lele.ioc.BeanFactory;
import com.lele.ioc.BeanFactoryImpl;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

@WebServlet("*.one")
public class DispatcherServlet extends ViewBaseServlet{

    private BeanFactory beanFactory; // 用beanFactory来创建所需类的实例

    @Override
    public void init() throws ServletException{
        super.init();
        beanFactory = (BeanFactory) getServletContext().getAttribute("beanFactory"); // 从上下文中拿对象工厂
    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp){
        // 1、拿请求路径 XXX
        String servletPath = req.getServletPath(); // 获取请求地址，打印：/XXX.后缀
        servletPath = servletPath.substring(1); // 去掉 /
        servletPath = servletPath.substring(0,servletPath.lastIndexOf(".one")); // 去掉 .后缀

        // 2、 将 XXX 和 servletController中的方法对应 （配置applicationContext.xml，在初始化方法里已经读取了该文件，这里直接拿）
        Object controllerBeanObj = beanFactory.getBean(servletPath); // 拿到 UserController 实例

        String flag = req.getParameter("flag");
        if (flag == null || flag == ""){ // 第一次进入走go
            flag = "go";
        }
        // 利用反射获取当前类的所有方法
        Method[] methods = controllerBeanObj.getClass().getDeclaredMethods();
        for (Method method :methods){
            String name = method.getName(); // 获取方法方法名字
            if (flag.equals(name)){
                try {
                    // 1、统一获取请求参数和值
                    // 这里需要先去设置里 Java compiler（-parameters） 设置虚拟机带形参名称
                    Parameter[] parameters = method.getParameters(); // 获得当前方法的所有参数
                    Object[] parameterValues = new Object[parameters.length]; // 用来存放参数的值
                    for (int i = 0; i < parameters.length; i++) {
                        Parameter parameter = parameters[i];
                        String parameterName = parameter.getName();
                        if ("req".equals(parameterName)){ // request参数 （注意名字和方法里参数的名字需一致）
                            parameterValues[i] = req;
                        } else if ("resp".equals(parameterName)){ // response参数
                            parameterValues[i] = resp;
                        } else if ("session".equals(parameterName)){ // session参数
                            parameterValues[i] = req.getSession();
                        } else { // 其他参数
                            String parameterValue = req.getParameter(parameterName);
                            // req.getParameterValues(parameterName); // 有复选框的时候用这个，它返回的是数组多个值，这里暂时不考虑使用
                            parameterValues[i] = parameterValue;
                        }
                    }
                    // 2、方法调用
                    method.setAccessible(true); // 设置私有方法也可访问
                    Object returnObj = method.invoke(controllerBeanObj,parameterValues); // 通过反射调用该方法，并接收返回值
                    // 3、处理视图
                    String methodReturnStr = (String) returnObj; // 强转
                    if (methodReturnStr.startsWith("redirect:")){ // 如果字符串以 redirect: 开头 （重定向）
                        String redirectStr = methodReturnStr.substring("redirect:".length()); // 截取 redirect: 后面的内容
                        resp.sendRedirect(redirectStr); // 做重定向
                    } else { // （转发）
                        /*
                         调用父类的模板处理方法，第一个参数：逻辑视图名称（模板名称）
                         thymeleaf会将这个 逻辑视图名称 对应到 物理视图名称 上去
                         物理视图名称：view-prefix + 逻辑视图名称 + view-suffix，所以真实的视图名称：/ index .html
                         这样就将web文件夹下的index.html文件上组装上了（跳转到该/index.html页面上了）
                        */
                        super.processTemplate(methodReturnStr,req,resp);
                    }
                    return;
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}
